#ifndef jfxcan_serialize_h
#define jfxcan_serialize_h

#include <iostream>
#include <string>
#include <vector>
#include <string>
#include <cmath>
#include "utils/jf_log.h"

class canReader{
public:
  void readField(uint64_t in, uint& u, int fromBit, int toBit) const {
    // bit位决定输入不能大于多少
    in = in << fromBit;
    in = in >> (63 - (toBit - fromBit));
    u = in;
  }
  void readField(uint64_t in, int& i, int fromBit, int toBit) const {
    // bit位决定输入不能大于多少
    in = in << fromBit;
    uint64_t judgeNeg = 1;
    judgeNeg = judgeNeg << 63;
    uint64_t mask = 1;
    mask = ~(mask << 63);
    int neg = 1;
    if((judgeNeg & in) != 0){
      in = in & mask;
      neg = -1;
    }
    in = in >> (63 - (toBit -fromBit));
    i = in * neg;
  }
  void readField(uint64_t in, float& f, int fromBit, int toBit) const {
    in = in << fromBit;
    in = in >> (63 - (toBit - fromBit));
    f = in;
    float n = std::pow(10, std::ceil(std::log(f) / std::log(10)));
    f = f / n;
  }
};

class canWriter{
public:
  void writeField(uint64_t& inout, uint i, int fromBit, int toBit) const {
    int len = toBit - fromBit + 1;
    unsigned int max = std::pow(2, len) - 1;
    // bit位决定输入不能大于多少
    if(i > max){
      JFLOG(JFERROR, "struct2can i > max : %u from:%d to:%d", i,fromBit, toBit);
      return;
    }
    uint64_t temp = i;
    temp = temp << (63 - toBit);
    inout |= temp;
  }
  void writeField(uint64_t& inout, int i, int fromBit, int toBit) const {
    int len = toBit - fromBit + 1;
    int max = std::pow(2, len - 1) - 1;
    // bit位决定输入不能大于多少
    if(i > max && i < -max){
      JFLOG(JFERROR, "struct2can i > max : %d from %d, to %d", i, fromBit, toBit);
      return;
    }
    uint64_t temp = 0;
    uint64_t mask = 1;
    if(i < 0) {
      temp = std::abs(i);
      mask = mask << (63 - fromBit);
    }else {
      temp = i;
      mask = 0;
    }
    temp = temp << (63 - toBit);
    temp = temp | mask;
    inout |= temp;
  }
  void writeField(uint64_t& inout, float f, int fromBit, int toBit) const {
    int len = toBit - fromBit + 1;
    int max = std::pow(2, len - 1) - 1;
    if(f > max){
      JFLOG(JFERROR, "struct2can i > max : %f from %d, to %d", f, fromBit, toBit);
      return;
    }
    int f2i = f;
    if(f == 0){
      writeField(inout, int(0), fromBit, toBit);
    }else {
      while(true){
        f = f* 10;
        if(f <= max){
          f2i = f;
        }else {
          break;
        }
      }
      writeField(inout, f2i, fromBit, toBit);
    }
  }
};

template<typename structType>
struct converter{};

#define canMetaData(structType, ...) \
  template<>\
  struct converter<structType> : public structType {\
    template<typename readerType>\
    void readRecursive(const readerType& reader, uint64_t in) {\
      return;\
    }\
    template<typename readerType, typename headType, typename... args>\
    void readRecursive(const readerType& reader, uint64_t in, const headType& h, const int& fromBit, const int& toBit, const args&... a) {\
      reader.readField(in, const_cast<headType&>(h),fromBit, toBit);\
      readRecursive(reader, in, a...);\
    }\
    template<typename readerType>\
    structType read(const readerType& reader, uint64_t in) {\
      readRecursive(reader, in, __VA_ARGS__);\
      return *this;\
    }\
    template<typename writerType>\
    void writeRecursive(const writerType& writer, uint64_t& inout) const {\
      return;\
    }\
    template<typename writerType, typename headType, typename... args>\
    void writeRecursive(const writerType& writer, uint64_t& inout, headType h, int fromBit, int toBit, args... a) const {\
      writer.writeField(inout, h, fromBit, toBit);\
      writeRecursive(writer, inout, a...);\
    }\
    template<typename writerType>\
    uint64_t write(const writerType& writer) const {\
      uint64_t ret = 0;\
      writeRecursive(writer, ret, __VA_ARGS__);\
      return ret;\
    }\
  };

template<typename structType>
uint64_t struct2can(const structType& in){
  canWriter w; 
  converter<structType> s = reinterpret_cast<const converter<structType>&>(in);
  uint64_t ret = s.write(w);
  return ret;
}

template<typename structType>
structType can2struct(uint64_t in){
  canReader r;
  converter<structType> ret;
  ret.read(r, in);
  return ret;
}

#endif